/*
Copyright (C) 2011 The University of Michigan
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Please send inquiries to powertutor@umich.edu
*/
package vn.cybersoft.obs.andriod.batterystats2.components;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.provider.Settings;
import android.os.Process;
import android.util.Log;
import android.util.DisplayMetrics;
import android.view.WindowManager;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.List;
import java.util.Random;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import vn.cybersoft.obs.andriod.batterystats2.phone.PhoneConstants;
import vn.cybersoft.obs.andriod.batterystats2.service.IterationData;
import vn.cybersoft.obs.andriod.batterystats2.service.PowerData;
import vn.cybersoft.obs.andriod.batterystats2.util.ForegroundDetector;
import vn.cybersoft.obs.andriod.batterystats2.util.NativeLoader;
import vn.cybersoft.obs.andriod.batterystats2.util.Recycler;
import vn.cybersoft.obs.andriod.batterystats2.util.SystemInfo;
public class OLED extends PowerComponent {
public static class OledData extends PowerData {
private static Recycler<OledData> recycler = new Recycler<OledData>();
public static OledData obtain() {
OledData result = recycler.obtain();
if (result != null)
return result;
return new OledData();
}
@Override
public void recycle() {
recycler.recycle(this);
}
public int brightness;
public double pixPower;
public boolean screenOn;
private OledData() {
}
public void init() {
this.screenOn = false;
}
public void init(int brightness, double pixPower) {
screenOn = true;
this.brightness = brightness;
this.pixPower = pixPower;
}
public void writeLogDataInfo(OutputStreamWriter out) throws IOException {
out.write("OLED-brightness " + brightness + "\n");
out.write("OLED-pix-power " + pixPower + "\n");
out.write("OLED-screen-on " + screenOn + "\n");
}
}
private static final String TAG = "OLED";
private static final String[] BACKLIGHT_BRIGHTNESS_FILES = {
"/sys/class/leds/lcd-backlight/brightness",
"/sys/devices/virtual/leds/lcd-backlight/brightness",
"/sys/devices/platform/trout-backlight.0/leds/lcd-backlight/brightness", };
private Context context;
private ForegroundDetector foregroundDetector;
private BroadcastReceiver broadcastReceiver;
private boolean screenOn;
private File frameBufferFile;
private int screenWidth;
private int screenHeight;
private static final int NUMBER_OF_SAMPLES = 500;
private int[] samples;
private String brightnessFile;
/*
* Coefficients pre-computed for pix power calculations.
*/
private double rcoef;
private double gcoef;
private double bcoef;
private double modul_coef;
public OLED(Context context, PhoneConstants constants) {
this.context = context;
screenOn = true;
foregroundDetector = new ForegroundDetector(
(ActivityManager) context
.getSystemService(context.ACTIVITY_SERVICE));
broadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
synchronized (this) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
screenOn = false;
} else if (intent.getAction().equals(
Intent.ACTION_SCREEN_ON)) {
screenOn = true;
}
}
};
};
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
context.registerReceiver(broadcastReceiver, intentFilter);
frameBufferFile = new File("/dev/fb0");
if (!frameBufferFile.exists()) {
frameBufferFile = new File("/dev/graphics/fb0");
}
if (frameBufferFile.exists())
try {
/* Check if we already have permission to read the frame buffer. */
boolean readOk = false;
try {
RandomAccessFile fin = new RandomAccessFile(
frameBufferFile, "r");
int b = fin.read();
fin.close();
readOk = true;
} catch (IOException e) {
}
/* Don't have permission, try to change permission as root. */
if (!readOk) {
java.lang.Process p = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(
p.getOutputStream());
os.writeBytes("chown " + android.os.Process.myUid() + " "
+ frameBufferFile.getAbsolutePath() + "\n");
os.writeBytes("chown app_"
+ (android.os.Process.myUid() - SystemInfo.AID_APP)
+ " " + frameBufferFile.getAbsolutePath() + "\n");
os.writeBytes("chmod 660 "
+ frameBufferFile.getAbsolutePath() + "\n");
os.writeBytes("exit\n");
os.flush();
p.waitFor();
if (p.exitValue() != 0) {
Log.i(TAG,
"failed to change permissions on frame buffer");
}
}
} catch (InterruptedException e) {
Log.i(TAG, "changing permissions on frame buffer interrupted");
} catch (IOException e) {
Log.i(TAG, "unexpected exception while changing permission on "
+ "frame buffer");
e.printStackTrace();
}
DisplayMetrics metrics = new DisplayMetrics();
WindowManager windowManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(metrics);
screenWidth = metrics.widthPixels;
screenHeight = metrics.heightPixels;
Random r = new Random();
samples = new int[NUMBER_OF_SAMPLES];
for (int i = 0; i < NUMBER_OF_SAMPLES; i++) {
int a = screenWidth * screenHeight * i / NUMBER_OF_SAMPLES;
int b = screenWidth * screenHeight * (i + 1) / NUMBER_OF_SAMPLES;
samples[i] = a + r.nextInt(b - a);
}
double[] channel = constants.oledChannelPower();
rcoef = channel[0] / 255 / 255;
gcoef = channel[1] / 255 / 255;
bcoef = channel[2] / 255 / 255;
modul_coef = constants.oledModulation() / 255 / 255 / 3 / 3;
for (int i = 0; i < BACKLIGHT_BRIGHTNESS_FILES.length; i++) {
if (new File(BACKLIGHT_BRIGHTNESS_FILES[i]).exists()) {
brightnessFile = BACKLIGHT_BRIGHTNESS_FILES[i];
}
}
}
@Override
protected void onExit() {
context.unregisterReceiver(broadcastReceiver);
super.onExit();
}
@Override
public IterationData calculateIteration(long iteration) {
IterationData result = IterationData.obtain();
boolean screen;
synchronized (this) {
screen = screenOn;
}
int brightness;
if (brightnessFile != null) {
brightness = (int) SystemInfo.getInstance().readLongFromFile(
brightnessFile);
} else {
try {
brightness = Settings.System.getInt(
context.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS);
} catch (Settings.SettingNotFoundException ex) {
Log.w(TAG, "Could not retrieve brightness information");
return result;
}
}
if (brightness < 0 || 255 < brightness) {
Log.w(TAG, "Could not retrieve brightness information");
return result;
}
double pixPower = 0;
if (screen && frameBufferFile.exists()) {
if (NativeLoader.jniLoaded()) {
pixPower = getScreenPixPower(rcoef, gcoef, bcoef, modul_coef);
} else
try {
RandomAccessFile fin = new RandomAccessFile(
frameBufferFile, "r");
for (int x : samples) {
fin.seek(x * 4);
int px = fin.readInt();
int b = px >> 8 & 0xFF;
int g = px >> 16 & 0xFF;
int r = px >> 24 & 0xFF;
/*
* Calculate the power usage of this one pixel if it
* were at full brightness. Linearly scale by brightness
* to get true power consumption. To calculate whole
* screen compute average of sampled region and multiply
* by number of pixels.
*/
int modul_val = r + g + b;
pixPower += rcoef * (r * r) + gcoef * (g * g) + bcoef
* (b * b) - modul_coef
* (modul_val * modul_val);
}
fin.close();
} catch (FileNotFoundException e) {
pixPower = -1;
} catch (IOException e) {
pixPower = -1;
e.printStackTrace();
}
if (pixPower >= 0) {
pixPower *= 1.0 * screenWidth * screenHeight
/ NUMBER_OF_SAMPLES;
}
}
OledData data = OledData.obtain();
if (!screen) {
data.init();
} else {
data.init(brightness, pixPower);
}
result.setPowerData(data);
if (screen) {
OledData uidData = OledData.obtain();
uidData.init(brightness, pixPower);
result.addUidPowerData(foregroundDetector.getForegroundUid(),
uidData);
}
return result;
}
@Override
public boolean hasUidInformation() {
return true;
}
@Override
public String getComponentName() {
return "OLED";
}
public static native double getScreenPixPower(double rcoef, double gcoef,
double bcoef, double modul_coef);
}